#ifndef __STD_COM_IMPL__
#define __STD_COM_IMPL__

#include "utility.h"

//////////////////////////////////////////////////////////////////////////

typedef		PROGID				TCLSIDREF;
#define		face_id(i)			uuidof(i)
#define		XCreateInstance		XmCreateInstance
#define		XIClassFactory		IKClassFactory

//////////////////////////////////////////////////////////////////////////
#define		DEFAULT_INIT_CLASS()	HRESULT init_class( IUnknown * , IUnknown * ) { return S_OK;}

inline bool operator < (const GUID& left, const GUID& right)
{
	return memcmp(&left, &right, sizeof(GUID)) < 0;
}
class UnkBase
{
public:
	ULONG		m_RefCount;
	UnkBase() : m_RefCount(0) {}
};

#define QIBEGIN	\
	STDMETHOD(QueryInterface)(REFGUID riid, void **ppv) {

#define QIUNKNOWN	\
	if(face_id(IUnknown) == riid) { *ppv = static_cast<IUnknown*>(this); AddRef(); return S_OK; }

#define QIUNKNOWN_(icast)	\
	if(face_id(IUnknown) == riid) { *ppv = static_cast<IUnknown*>(static_cast<icast*>(this)); AddRef(); return S_OK; }

#define QIENTRY(iface)	\
	if(face_id(iface) == riid) { *ppv = static_cast<iface*>(this); AddRef(); return S_OK; }

#define QIENTRY_(iface, icast)	\
	if(face_id(iface) == riid) { *ppv = static_cast<iface*>(static_cast<icast*>(this)); AddRef(); return S_OK; }

#define QIEND \
	return E_NOINTERFACE; }

#if !defined(WIN32) && !defined(WIN64)

#define ADDREF	\
	STDMETHOD_(ULONG, AddRef)()	\
	{ return ++this->m_RefCount; }

#define RELEASE	\
	STDMETHOD_(ULONG, Release)()	\
	{ if(--this->m_RefCount) return this->m_RefCount; delete this; return 0; }

#else

#define ADDREF		STDMETHOD_(ULONG, AddRef)()	\
					{ InterlockedIncrement( (PLONG)&m_RefCount ); return m_RefCount; }

#define RELEASE		STDMETHOD_(ULONG, Release )() {	\
					LONG lRet = InterlockedDecrement( (PLONG)&m_RefCount );\
					if( lRet == 0 ) delete this;\
					return lRet;\
}
#endif

#define UNKNOWN_IMP_SPEC(entrys) \
	QIBEGIN  QIUNKNOWN  entrys  QIEND  ADDREF  RELEASE

#define UNKNOWN_IMP_SPEC_(icast, entrys) \
	QIBEGIN  QIUNKNOWN_(icast)  entrys  QIEND  ADDREF  RELEASE

#define UNKNOWN_IMP0() \
	UNKNOWN_IMP_SPEC( ; )
#define UNKNOWN_IMP1(i1) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) )
#define UNKNOWN_IMP2(i1, i2) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) )
#define UNKNOWN_IMP3(i1, i2, i3) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) )
#define UNKNOWN_IMP4(i1, i2, i3, i4) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) )
#define UNKNOWN_IMP5(i1, i2, i3, i4, i5) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) )
#define UNKNOWN_IMP6(i1, i2, i3, i4, i5, i6) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) )
#define UNKNOWN_IMP7(i1, i2, i3, i4, i5, i6, i7) \
	UNKNOWN_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7) )


#define UNKNOWN_IMP1_(i1) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) )
#define UNKNOWN_IMP2_(i1, i2) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) )
#define UNKNOWN_IMP3_(i1, i2, i3) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) )
#define UNKNOWN_IMP4_(i1, i2, i3, i4) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) )
#define UNKNOWN_IMP5_(i1, i2, i3, i4, i5) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) )
#define UNKNOWN_IMP6_(i1, i2, i3, i4, i5, i6) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) )
#define UNKNOWN_IMP7_(i1, i2, i3, i4, i5, i6, i7) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7) )
#define UNKNOWN_IMP8_(i1, i2, i3, i4, i5, i6, i7, i8) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7)  QIENTRY(i8) )
#define UNKNOWN_IMP9_(i1, i2, i3, i4, i5, i6, i7, i8, i9 ) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7)  QIENTRY(i8) QIENTRY(i9) )
#define UNKNOWN_IMP10_(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10 ) \
	UNKNOWN_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7)  QIENTRY(i8) QIENTRY(i9) QIENTRY(i10) )
//////////////////////////////////////////////////////////////////////////


class IUnknown_Nondelegate
{
public:
	STDMETHOD(QueryInterface_Nondelegate)(REFGUID riid, void **ppv) = 0;
	STDMETHOD_(ULONG, AddRef_Nondelegate)() = 0;
	STDMETHOD_(ULONG, Release_Nondelegate)() = 0;
};


class CUnknownImp_Inner
{
public:
	ULONG m_RefCount;
	CUnknownImp_Inner(): m_RefCount(0), m_punkOuter(0) {}
public:
	IUnknown *m_punkOuter;
	HRESULT init_class_inner(IUnknown *punkOuter)
	{
		m_punkOuter = punkOuter;
		return S_OK;
	}
};


#define QIBEGIN_NONDELEGATE	\
	STDMETHOD(QueryInterface_Nondelegate)(REFGUID riid, void **ppv) {

#define QIUNKNOWN_NONDELEGATE	\
	if(face_id(IUnknown) == riid) { *ppv = static_cast<IUnknown_Nondelegate*>(this); AddRef_Nondelegate(); return S_OK; }

#define QIUNKNOWN_NONDELEGATE_(icast)	\
	if(face_id(IUnknown) == riid) { *ppv = static_cast<IUnknown_Nondelegate*>(static_cast<icast*>(this)); AddRef_Nondelegate(); return S_OK; }

#define ADDREF_NONDELEGATE	\
	STDMETHOD_(ULONG, AddRef_Nondelegate)()	\
	{ return ++this->m_RefCount; }

#define RELEASE_NONDELEGATE	\
	STDMETHOD_(ULONG, Release_Nondelegate)()	\
	{ if(--this->m_RefCount) return this->m_RefCount; delete this; return 0; }

#define QI_INNER	\
	STDMETHOD(QueryInterface)(REFGUID riid, void **ppv)	\
	{ if(this->m_punkOuter) { return this->m_punkOuter->QueryInterface(riid, ppv); } else { return QueryInterface_Nondelegate(riid, ppv); } }

#define ADDREF_INNER	\
	STDMETHOD_(ULONG, AddRef)()	\
	{ if(this->m_punkOuter) { return this->m_punkOuter->AddRef(); } else { return AddRef_Nondelegate(); } }

#define RELEASE_INNER	\
	STDMETHOD_(ULONG, Release)()	\
	{ if(this->m_punkOuter) { return this->m_punkOuter->Release(); } else { return Release_Nondelegate(); } }

#define UNKNOWN_INNER_IMP_SPEC(entrys) \
	QIBEGIN_NONDELEGATE  QIUNKNOWN_NONDELEGATE  entrys  QIEND  ADDREF_NONDELEGATE  RELEASE_NONDELEGATE  QI_INNER  ADDREF_INNER  RELEASE_INNER

#define UNKNOWN_INNER_IMP_SPEC_(icast, entrys) \
	QIBEGIN_NONDELEGATE  QIUNKNOWN_NONDELEGATE_(icast)  entrys  QIEND  ADDREF_NONDELEGATE  RELEASE_NONDELEGATE  QI_INNER  ADDREF_INNER  RELEASE_INNER

#define UNKNOWN_INNER_IMP1(i) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i) )
#define UNKNOWN_INNER_IMP2(i1, i2) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) )
#define UNKNOWN_INNER_IMP3(i1, i2, i3) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) )
#define UNKNOWN_INNER_IMP4(i1, i2, i3, i4) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) )
#define UNKNOWN_INNER_IMP5(i1, i2, i3, i4, i5) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) )
#define UNKNOWN_INNER_IMP6(i1, i2, i3, i4, i5, i6) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) )
#define UNKNOWN_INNER_IMP7(i1, i2, i3, i4, i5, i6, i7) \
	UNKNOWN_INNER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7) )

#define UNKNOWN_INNER_IMP2_(i1, i2) \
	UNKNOWN_INNER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) )
#define UNKNOWN_INNER_IMP3_(i1, i2, i3) \
	UNKNOWN_INNER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) )
#define UNKNOWN_INNER_IMP4_(i1, i2, i3, i4) \
	UNKNOWN_INNER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) )
#define UNKNOWN_INNER_IMP5_(i1, i2, i3, i4, i5) \
	UNKNOWN_INNER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) )
#define UNKNOWN_INNER_IMP6_(i1, i2, i3, i4, i5, i6) \
	UNKNOWN_INNER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) )
#define UNKNOWN_INNER_IMP7_(i1, i2, i3, i4, i5, i6, i7) \
	UNKNOWN_INNER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7) )

class CUnknownImp_Outer
{
public:
	ULONG m_RefCount;
	CUnknownImp_Outer(): m_RefCount(0) {}
public:
	xm::iptr<IUnknown> m_punkInner;
	HRESULT init_class_outer(IUnknown *prot, IUnknown *punkThisOuter, TCLSIDREF rclsid)
	{
		return XCreateInstance( rclsid, punkThisOuter, face_id(IUnknown), (void**)&m_punkInner, prot);
	}
};

#define QIEND_OUTER \
	return this->m_punkInner->QueryInterface(riid, ppv); }

#define UNKNOWN_OUTER_IMP_SPEC(entrys) \
	QIBEGIN  QIUNKNOWN  entrys  QIEND_OUTER  ADDREF  RELEASE

#define UNKNOWN_OUTER_IMP_SPEC_(icast, entrys) \
	QIBEGIN  QIUNKNOWN_(icast)  entrys  QIEND_OUTER  ADDREF  RELEASE

#define UNKNOWN_OUTER_IMP0 \
	QIBEGIN  QIUNKNOWN  QIEND_OUTER  ADDREF  RELEASE
#define UNKNOWN_OUTER_IMP1(i) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i) )
#define UNKNOWN_OUTER_IMP2(i1, i2) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) )
#define UNKNOWN_OUTER_IMP3(i1, i2, i3) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) )
#define UNKNOWN_OUTER_IMP4(i1, i2, i3, i4) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) )
#define UNKNOWN_OUTER_IMP5(i1, i2, i3, i4, i5) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) )
#define UNKNOWN_OUTER_IMP6(i1, i2, i3, i4, i5, i6) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) )
#define UNKNOWN_OUTER_IMP7(i1, i2, i3, i4, i5, i6, i7) \
	UNKNOWN_OUTER_IMP_SPEC( QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7) )

#define UNKNOWN_OUTER_IMP2_(i1, i2) \
	UNKNOWN_OUTER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) )
#define UNKNOWN_OUTER_IMP3_(i1, i2, i3) \
	UNKNOWN_OUTER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) )
#define UNKNOWN_OUTER_IMP4_(i1, i2, i3, i4) \
	UNKNOWN_OUTER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) )
#define UNKNOWN_OUTER_IMP5_(i1, i2, i3, i4, i5) \
	UNKNOWN_OUTER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) )
#define UNKNOWN_OUTER_IMP6_(i1, i2, i3, i4, i5, i6) \
	UNKNOWN_OUTER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) )
#define UNKNOWN_OUTER_IMP7_(i1, i2, i3, i4, i5, i6, i7) \
	UNKNOWN_OUTER_IMP_SPEC_(i1, QIENTRY(i1) QIENTRY(i2) QIENTRY(i3) QIENTRY(i4) QIENTRY(i5) QIENTRY(i6) QIENTRY(i7) )


//////////////////////////////////////////////////////////////////////////


template<class CLS>
class TStdClsFactory : public XIClassFactory, private UnkBase
{
public: // IUnknown:
	UNKNOWN_IMP1(XIClassFactory);

public: // std factory invoke:
    virtual ~TStdClsFactory(){}
	STDMETHOD(init_class)(IUnknown* /*prot*/, IUnknown* punkOuter)
	{
		return !punkOuter ? S_OK : E_INVALIDARG;
	}

public: // XIClassFactory:
	STDMETHOD(CreateInstance)(IUnknown *prot, IUnknown *punkOuter, const IID& riid, void **ppv)
	{
		return create_instance(prot, punkOuter, riid, ppv);
	}
	STDMETHOD(LockServer)(BOOL fLock)
	{
		return lock_server(fLock);
	}
public:
	static HRESULT create_instance(IUnknown *prot, IUnknown *punkOuter, const IID& riid, void **ppv)
	{
		*ppv = 0;
		// aggregation validate:
		//RASSERT(!punkOuter || face_id(IUnknown) == riid, E_INVALIDARG);
		// create new object/aggregation:
		UTIL::sentry<CLS*> p(new CLS);
		RASSERT(p, E_UNEXPECTED);
		((IUnknown_Nondelegate*)(CLS*)p)->AddRef_Nondelegate(); // nondelegation, protect reference count
		RFAILED(p->init_class(prot, punkOuter));
		HRESULT hr = ((IUnknown_Nondelegate*)(CLS*)p)->QueryInterface_Nondelegate(riid, ppv);
		((IUnknown_Nondelegate*)p.detach())->Release_Nondelegate(); // nondelegation, balance reference count or destroy.
		return hr;
	}
	static HRESULT lock_server(BOOL /*fLock*/)
	{
		return S_OK;
	}
	static HRESULT GetClassObject(const IID& riid, void **ppv)
	{
		return TStdClsFactory<TStdClsFactory<CLS> >::create_instance(0, 0, riid, ppv);
	}
};

//////////////////////////////////////////////////////////////////////////


#ifndef GUID_NULL
DEFINE_GUID(GUID_NULL,
			0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
#endif


#endif